package org.xbdframework.dao.core.sqlandparam;

import java.util.*;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.util.ObjectUtils;

import org.xbdframework.dao.core.BasePo;

public abstract class AbstractSqlAndParamBuilder<T extends BasePo>
		implements SqlAndParamBuilder {

	protected final static String SQL_KEYWORD_WHERE = "where";

	protected Logger logger = LoggerFactory.getLogger(getClass());

	protected final T po;

	protected final List<String> setColumns = new ArrayList<>();

	protected final List<Object> setValues = new ArrayList<>();

	public AbstractSqlAndParamBuilder(T po) {
		this.po = po;
	}

	public AbstractSqlAndParamBuilder set(String column, Object value) {
		return set(column, value, true);
	}

	public AbstractSqlAndParamBuilder set(String column, Object value, boolean isSet) {
		if (isSet) {
			this.setColumns.add(column);
			this.setValues.add(value);
		}

		return this;
	}

	public AbstractSqlAndParamBuilder set(Map<String, Object> map) {
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			set(entry.getKey(), entry.getValue());
		}

		return this;
	}

	protected Object[] buildPkWhereArrayParams(StringBuilder sb) {
		sb.append(this.po.getPkName()).append(" = ?");
		return new Object[] { this.po.getPkValue() };
	}

	protected Object[] buildPkWhereArrayParams() {
		return new Object[] { this.po.getPkValue() };
	}

	protected Map<String, Object> buildPkWhereMapParams() {
		Map<String, Object> params = new HashMap<>();

		params.put(this.po.getPkName(), this.po.getPkValue());

		return params;
	}

	protected SqlParameterSource buildPkWhereSqlParameterSourceParams() {
		MapSqlParameterSource parameterSource = new MapSqlParameterSource();

		parameterSource.addValue(this.po.getPkName(), this.po.getPkValue());

		return parameterSource;
	}

	protected Object[] mergeMapParameter(Object[] params1, Object[] params2) {
		Object[] params = new Object[0];

		params = ArrayUtils.addAll(params, params1);
		params = ArrayUtils.addAll(params, params2);

		return params;
	}

	protected Map<String, Object> mergeMapParameter(Map<String, Object> params1,
			Map<String, Object> params2) {
		Map<String, Object> params = new HashMap<>();

		if (!ObjectUtils.isEmpty(params1)) {
			params.putAll(params1);
		}

		if (!ObjectUtils.isEmpty(params2)) {
			params.putAll(params2);
		}

		return params;
	}

	protected SqlParameterSource mergeParameterSource(SqlParameterSource params1,
			SqlParameterSource params2) {
		MapSqlParameterSource parameterSource = new MapSqlParameterSource();

		Map<String, Object> extractParameters1 = extractCaseSensitiveParameters(params1);
		Map<String, Object> extractParameters2 = extractCaseSensitiveParameters(params2);

		parameterSource.addValues(extractParameters1);
		parameterSource.addValues(extractParameters2);

		return parameterSource;
	}

	protected Map<String, Object> extractCaseSensitiveParameters(
			SqlParameterSource parameterSource) {
		Map<String, String> caseSensitiveParameterNames = SqlParameterSourceUtils
				.extractCaseInsensitiveParameterNames(parameterSource);

		Map<String, Object> caseSensitiveParameters = new HashMap<>();

		if (!ObjectUtils.isEmpty(caseSensitiveParameterNames)) {
			caseSensitiveParameterNames.forEach((lowercaseName, name) -> {
				if (parameterSource.hasValue(name)) {
					caseSensitiveParameters.put(name, parameterSource.getValue(name));
				}
				else {
					this.logger.warn("parameter key {} not have a value.",
							new Object[] { name });
				}
			});
		}

		return caseSensitiveParameters;
	}

	protected String escapeWhere(String where) {
		if (StringUtils.isBlank(where)) {
			return null;
		}

		// 如果没有注明where关键字，则加上
		if (!where.contains(SQL_KEYWORD_WHERE)) {
			where = " " + SQL_KEYWORD_WHERE + " " + where.trim();
		}

		return where;
	}

	protected String getTableName() {
		return this.po.getTableName();
	}

}
